package e2e

import (
	"os"
	"os/exec"
	"path"
	"strings"
	"time"

	"github.com/Netflix/go-expect"
	"github.com/hinshun/vt10x"
	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	k8sruntime "k8s.io/apimachinery/pkg/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/config"

	oamcore "github.com/oam-dev/kubevela/apis/core.oam.dev"
)

var rudrPath = GetCliBinary()

//GetCliBinary is to build kubevela binary.
func GetCliBinary() string {
	cwd, _ := os.Getwd()
	return path.Join(cwd, "../..", "./bin")
}

// Exec executes a command
func Exec(cli string) (string, error) {
	var output []byte
	session, err := asyncExec(cli)
	if err != nil {
		return string(output), err
	}
	s := session.Wait(30 * time.Second)
	return string(s.Out.Contents()) + string(s.Err.Contents()), nil
}

// ExecAndTerminate executes a long-running command and terminate it after 3s
func ExecAndTerminate(cli string) (string, error) {
	var output []byte
	session, err := asyncExec(cli)
	if err != nil {
		return string(output), err
	}
	time.Sleep(3 * time.Second)
	s := session.Terminate()
	return string(s.Out.Contents()) + string(s.Err.Contents()), nil
}

// LongTimeExec executes a long-running command and wait it exits by itself
func LongTimeExec(cli string, timeout time.Duration) (string, error) {
	var output []byte
	session, err := asyncExec(cli)
	if err != nil {
		return string(output), err
	}
	s := session.Wait(timeout)
	return string(s.Out.Contents()) + string(s.Err.Contents()), nil
}

func asyncExec(cli string) (*gexec.Session, error) {
	c := strings.Fields(cli)
	commandName := path.Join(rudrPath, c[0])
	command := exec.Command(commandName, c[1:]...)
	session, err := gexec.Start(command, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter)
	return session, err
}

// InteractiveExec executes a command with interactive input
func InteractiveExec(cli string, consoleFn func(*expect.Console)) (string, error) {
	var output []byte
	console, _, err := vt10x.NewVT10XConsole(expect.WithStdout(ginkgo.GinkgoWriter))
	gomega.Expect(err).NotTo(gomega.HaveOccurred())
	defer console.Close()
	doneC := make(chan struct{})

	go func() {
		defer ginkgo.GinkgoRecover()
		defer close(doneC)
		consoleFn(console)
	}()

	c := strings.Fields(cli)
	commandName := path.Join(rudrPath, c[0])
	command := exec.Command(commandName, c[1:]...)
	command.Stdin = console.Tty()

	session, err := gexec.Start(command, console.Tty(), console.Tty())
	s := session.Wait(300 * time.Second)
	console.Tty().Close()
	<-doneC
	if err != nil {
		return string(output), err
	}
	return string(s.Out.Contents()) + string(s.Err.Contents()), nil
}

func NewK8sClient() (client.Client, error) {
	conf, err := config.GetConfig()
	if err != nil {
		return nil, err
	}
	scheme := k8sruntime.NewScheme()
	if err := clientgoscheme.AddToScheme(scheme); err != nil {
		return nil, err
	}
	if err := oamcore.AddToScheme(scheme); err != nil {
		return nil, err
	}

	k8sclient, err := client.New(conf, client.Options{Scheme: scheme})
	if err != nil {
		return nil, err
	}
	return k8sclient, nil
}

func BeforeSuit() {
	// sync capabilities from cluster into local
	_, _ = Exec("vela workloads")
}
